package com.link.base.core.basic.config.mvc;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.link.core.basic.interceptor.SecurityInterceptor;
import com.link.core.xstream.MyXStreamMarshaller;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.StaxDriver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.view.BeanNameViewResolver;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.springframework.web.servlet.view.xml.MarshallingView;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * SpringMVC配置类
 *
 * @author 李杨
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.link.**.controller"},
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)})
public class AppContextMvcConfig implements WebMvcConfigurer {

    /**
     * 配置解析器，主要是对json、xml、html的处理
     *
     * @author 李杨
     */
    @Bean
    public ContentNegotiatingViewResolver contentNegotiatingViewResolver() {
        ContentNegotiatingViewResolver contentNegotiatingViewResolver = new ContentNegotiatingViewResolver();
        contentNegotiatingViewResolver.setOrder(1);

        Map<String, MediaType> pecnsArgs = new HashMap<String, MediaType>();
        pecnsArgs.put("json", MediaType.APPLICATION_JSON);
        pecnsArgs.put("html", MediaType.TEXT_HTML);
        pecnsArgs.put("xml", MediaType.APPLICATION_XML);

        PathExtensionContentNegotiationStrategy pecns = new PathExtensionContentNegotiationStrategy(pecnsArgs);
        ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(pecns);

        contentNegotiatingViewResolver.setContentNegotiationManager(contentNegotiationManager);

        List<View> viewList = new ArrayList<View>();
        viewList.add(new MappingJackson2JsonView());
        MarshallingView marshallingView = new MarshallingView();
        marshallingView.setMarshaller(xStreamMarshaller());
        viewList.add(marshallingView);
        contentNegotiatingViewResolver.setDefaultViews(viewList);

        return contentNegotiatingViewResolver;
    }

    /**
     * 视图解析器
     *
     * @author 李杨
     */
    @Bean
    public BeanNameViewResolver beanNameViewResolver() {
        BeanNameViewResolver beanNameViewResolver = new BeanNameViewResolver();
        return beanNameViewResolver;
    }

    /**
     * 添加自定义参数处理器
     *
     * @author 李杨
     * @param resolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        // 配置自定义接收参数
//        super.addArgumentResolvers(resolvers);
        resolvers.add(new JsonParamArgumentResolver());
    }

    /**
     * 配置自定义转换器
     *
     * @author 李杨
     * @param converters
     */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        //清空转换器，如果跟其它系统结合，可能需要其它默认转换器，如果有问题可考虑去掉 converters.clear();
        converters.clear();
        converters.add(0, jsonHttpMessageConverter());
        converters.add(1, marshallingHttpMessageConverter());
        converters.add(2, resourceHttpMessageConverter());
    }

    /**
     * json转换器
     *
     * @author 李杨
     * @return
     */
    public MappingJackson2HttpMessageConverter jsonHttpMessageConverter() {
        MappingJackson2HttpMessageConverter jsonHttpMessageConverter = new MappingJackson2HttpMessageConverter();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ObjectMapper objectMapper = new CustomerLongTypeMapper();
        objectMapper.setDateFormat(simpleDateFormat);
        //dms 应该要注释掉下面的这一句
        objectMapper.setSerializationInclusion(Include.NON_NULL);
        jsonHttpMessageConverter.setObjectMapper(objectMapper);
        List<MediaType> mediaTypeList = new ArrayList<MediaType>();
        mediaTypeList.add(MediaType.ALL);
        mediaTypeList.add(MediaType.APPLICATION_JSON_UTF8);
        mediaTypeList.add(MediaType.TEXT_HTML);
        jsonHttpMessageConverter.setSupportedMediaTypes(mediaTypeList);
        return jsonHttpMessageConverter;
    }

    /**
     * xml转换器
     *
     * @author 李杨
     * @return
     */
    public MarshallingHttpMessageConverter marshallingHttpMessageConverter() {
        MarshallingHttpMessageConverter marshallingHttpMessageConverter = new MarshallingHttpMessageConverter();
        marshallingHttpMessageConverter.setMarshaller(xStreamMarshaller());
        marshallingHttpMessageConverter.setUnmarshaller(xStreamMarshaller());
        List<MediaType> mediaTypeList = new ArrayList<MediaType>();
        mediaTypeList.add(MediaType.APPLICATION_XML);
        marshallingHttpMessageConverter.setSupportedMediaTypes(mediaTypeList);
        return marshallingHttpMessageConverter;
    }

    /**
     * 静态资源转换器
     *
     * @author 李杨
     * @return
     */
    public ResourceHttpMessageConverter resourceHttpMessageConverter() {
        ResourceHttpMessageConverter resourceHttpMessageConverter = new ResourceHttpMessageConverter();
        List<MediaType> mediaTypeList = new ArrayList<MediaType>();
        mediaTypeList.add(MediaType.IMAGE_JPEG);
        //原配置里的MediaType为application/csv，application/x-excel，application/pdf，application/x-jpg，目前暂时没有找到对应的类型
        mediaTypeList.add(MediaType.MULTIPART_FORM_DATA);
        resourceHttpMessageConverter.setSupportedMediaTypes(mediaTypeList);
        return resourceHttpMessageConverter;
    }

    /**
     * xml转换器
     *
     * @author 李杨
     * @return
     */
    public MyXStreamMarshaller xStreamMarshaller() {
        MyXStreamMarshaller xStreamMarshaller = new MyXStreamMarshaller();
        xStreamMarshaller.setAutodetectAnnotations(true);

        StaxDriver staxDiver = new StaxDriver(new NoNameCoder());
        xStreamMarshaller.setStreamDriver(staxDiver);

        return xStreamMarshaller;
    }

    @Bean
    public RequestMappingHandlerMapping defaultAnnotationHandlerMapping() {
        return new RequestMappingHandlerMapping();
    }

    /**
     * 文件上传配置
     *
     * @return
     */
    @Bean
    public CommonsMultipartResolver multipartResolver() {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
        //设置大小为不限
        multipartResolver.setMaxUploadSize(102400000);
        multipartResolver.setMaxInMemorySize(102400000);
        return multipartResolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        //配置静态文件处理
        configurer.enable();
    }

    /**
     * 拦截请求，获取得到token中的信息
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加mvc拦截器，拦截所有请求，解析得到token里面的信息。
        registry.addInterceptor(securityInterceptor()).addPathPatterns("/**");
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
    }

    @Bean
    public SecurityInterceptor securityInterceptor() {
        return new SecurityInterceptor();
    }
}
